﻿using System;
using System.Collections.Generic;
using System.Text;

namespace SuperX.Common.Helper
{
    /// <summary>
    /// 雪花漂移算法
    /// </summary>
    public class IdFactory
    {
        public IdFactory(long workerId, long datacenterId)
        {
            if (workerId > IdFactory.maxWorkerId || workerId < 0L)
            {
                throw new ArgumentException("Id can't be greater than " + IdFactory.maxWorkerId.ToString() + " or less than 0");
            }
            if (datacenterId > IdFactory.maxDatacenterId || datacenterId < 0L)
            {
                throw new ArgumentException("datacenter Id can't be greater than " + IdFactory.maxDatacenterId.ToString() + " or less than 0");
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }

        // 
        public long NextId()
        {
            long result;
            lock (syncRoot)
            {
                long timestamp = this.TimeGen();
                bool steal;
                if (timestamp < this.lastTimestamp)
                {
                    timestamp = this.lastTimestamp;
                    steal = true;
                }
                else
                {
                    steal = false;
                }
                if (this.lastTimestamp == timestamp)
                {
                    this.sequence = (this.sequence + 1L) & this.sequenceMask;
                    if (this.sequence == 0L)
                    {
                        if (steal)
                        {
                            //如果超过一毫秒的Id数量上限，就向未来偷取一毫秒
                            timestamp = this.TimeGenStealNextMillis(this.lastTimestamp);
                        }
                        else
                        {
                            timestamp = this.TilNextMillis(this.lastTimestamp);
                        }
                    }
                }
                else
                {
                    this.sequence = 0L;
                }
                this.lastTimestamp = timestamp;
                result = (timestamp - IdFactory.twepoch << (int)this.timestampLeftShift | this.datacenterId << (int)this.datacenterIdShift | this.workerId << (int)this.workerIdShift | this.sequence);
            }
            return result;
        }

        protected long TilNextMillis(long lastTimestamp)
        {
            long num;
            for (num = this.TimeGen(); num <= lastTimestamp; num = this.TimeGen())
            {
            }
            return num;
        }

        // 
        protected long TimeGen()
        {
            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
        }

        // 
        protected long TimeGenStealNextMillis(long currentTimeGen)
        {
            return currentTimeGen++;
        }

        private long workerId;
        private long datacenterId;
        private long sequence;

        private static long twepoch = 1288834974657L;
        private static long workerIdBits = 5L;

        private static long datacenterIdBits = 5L;
        private static long maxWorkerId = -1L ^ -1L << (int)IdFactory.workerIdBits;
        private static long maxDatacenterId = -1L ^ -1L << (int)IdFactory.datacenterIdBits;
        private static long sequenceBits = 12L;
        private long workerIdShift = IdFactory.sequenceBits;
        private long datacenterIdShift = IdFactory.sequenceBits + IdFactory.workerIdBits;
        private long timestampLeftShift = IdFactory.sequenceBits + IdFactory.workerIdBits + IdFactory.datacenterIdBits;
        private long sequenceMask = -1L ^ -1L << (int)IdFactory.sequenceBits;
        private long lastTimestamp = -1L;
        private static object syncRoot = new object();
    }
}
